#ifndef PW_PBL_CONCEPTS_FUNCTION_HPP
#define PW_PBL_CONCEPTS_FUNCTION_HPP

#include "Pack.hpp"

namespace pw_pbl::inline Concepts
{
	template <typename FUNCTION>
	struct ttFunctionTraits;

	template <typename R, typename... T>
	struct ttFunctionTraits<R(T...)> : public ttPack<T...>
	{
		using result_type = R;
	};

	template <typename R, typename... T, bool EXCEPT>
	struct ttFunctionTraits<R (*)(T...) noexcept(EXCEPT)> : public ttFunctionTraits<R(T...)>
	{
	};

	template <typename R, typename... T, bool EXCEPT>
	struct ttFunctionTraits<R (&)(T...) noexcept(EXCEPT)> : public ttFunctionTraits<R(T...)>
	{
	};

	template <typename O, typename R, typename... T, bool EXCEPT>
	struct ttFunctionTraits<R (O::*)(T...) noexcept(EXCEPT)> : public ttFunctionTraits<R(T...)>
	{
		using object_type = O;
	};

	template <typename O, typename R, typename... T, bool EXCEPT>
	struct ttFunctionTraits<R (O::*)(T...) const noexcept(EXCEPT)> : public ttFunctionTraits<R(T...)>
	{
		using object_type = const O;
	};

	template <typename O, typename R, typename... T, bool EXCEPT>
	struct ttFunctionTraits<R (O::*)(T...) & noexcept(EXCEPT)> : public ttFunctionTraits<R(T...)>
	{
		using object_type = O &;
	};

	template <typename O, typename R, typename... T, bool EXCEPT>
	struct ttFunctionTraits<R (O::*)(T...) const & noexcept(EXCEPT)> : public ttFunctionTraits<R(T...)>
	{
		using object_type = const O &;
	};

	template <typename O, typename R, typename... T, bool EXCEPT>
	struct ttFunctionTraits<R (O::*)(T...) && noexcept(EXCEPT)> : public ttFunctionTraits<R(T...)>
	{
		using object_type = O &&;
	};

	template <typename O, typename R, typename... T, bool EXCEPT>
	struct ttFunctionTraits<R (O::*)(T...) const && noexcept(EXCEPT)> : public ttFunctionTraits<R(T...)>
	{
		using object_type = const O &&;
	};

} // namespace pw_pbl::inline Concepts
#endif // PW_PBL_CONCEPTS_FUNCTION_HPP
